<?php

namespace StudyBuddy;

use \StudyBuddy\Forms\Form;

/**
 * This abstract class is responsible for generating
 * an output page.
 *
 * This is an abstract class and must be extended by the
 * actual controller class
 *
 * The controller class must implement the main() method which
 * will be called by this class automatically
 * after some important pre-processing and initialization
 * have taken place: for example that required variables have
 * all been submitted (if any)
 * Also if controller has a specific $permission
 * set as instance variable
 * this class will check that the current user
 * (represented as Viewer object)
 * has required access level to perform this 'permission'
 * an ACL is used for permission check and is based on user group membership
 * which is called 'role' in ACL jargon.
 */
abstract class WebPage extends Base {

    /**
     * HTTP Response code to send
     * the value of 200 is default
     * This is usefull only when handling certain
     * exceptions that may indicate a '404 not found' error
     * in which case we pass the 404 as error code
     * of exception and then can set the http 404 response
     *
     * @var int
     */
    protected $httpCode = 200;

    /**
     * Array of required GET or POST
     * parameters.
     * These must not be empty
     *
     * @var array
     */
    protected $aRequired = array();

    /**
     * Object representing Array of QUERY_STRING params
     * this is GET or POST array
     *
     * @var object of type Request
     */
    protected $oRequest;

    /**
     * Flag indicates that
     * the page being rendered for a mobile
     * device. This means content should
     * be in a short format - titles only
     * for a list of articles, etc.
     *
     * @var bool
     */
    protected $isMobile = false;

    /**
     * Array holds
     * links generated by pager class
     *
     * @var
     */
    protected $arrPagerLinks;

    /**
     * Links generated by Paginator
     *
     * @var string html to show pagination links
     */
    protected $pagerLinks = '';

    /**
     * Flag indicates that REQUEST_METHOD
     * MUST be POST
     *
     * @var bool
     */
    protected $bRequirePost = false;

    /**
     * extra javascript(s) to add to this page
     * if class extending this class has this property,
     * then value will be added to the bottom
     * of the page as value of script tag
     *
     * @var mixed string or array of strings
     */
    protected $lastJs;

    /**
     * Extra css files to add for the page
     *
     * @var mixed string (path to .css file) or
     * array of such strings
     */
    protected $extraCss;

    /**
     * Flag indicates that
     * we require to validate a form token
     *
     * @var bool
     */
    protected $requireToken = false;

    /**
     * Name of template dir
     * for mobile output it should
     * be dynamically chaned to 'mobile'
     * it can also be changed to 'tablet'
     * for tablet screens
     *
     * @var string
     */
    protected $tplDir = 'www';

    /**
     *
     * This is for skinning/styling support
     * right now we only have 1 style,
     * it has id = 1
     * @var mixed int | numeric string
     */
    protected $styleID = '2';

    /**
     *
     * layoutID 1 means 1-pane page: no nav div, just one main div
     * layoutID 2 means 2-pane page: main div and nav div
     *
     * @var mixed int | numeric string
     */
    protected $layoutID = '2';

    /**
     * Array of replacement vars
     * for the tplMain template
     *
     * @var array
     */
    protected $aPageVars;

    /**
     * Controller may override this
     * to skip initPageVars() method
     * This is helpful when controller is called by Ajax only
     * so int's not necessary to initialize page vars
     *
     * @var bool
     */
    protected $bInitPageVars = true;

    /**
     * Translator object
     *
     * @var object of type \I18n\Translator
     */
    protected $Tr;
    protected $action;

    /**
     * Constructor
     * @return
     * @param object $oRegistry
     * @param object $oRequest
     */
    public function __construct(Registry $oRegistry, Request $oRequest = null) {

        parent::__construct($oRegistry);

        $this->oRequest = (null !== $oRequest) ? $oRequest : $oRegistry->Request;
        $this->action = $this->oRequest['a'];

        $this->initParams()
                ->setTemplateDir()
                ->initViewerObject()
                ->setLocale()
                ->loginByFacebookCookie()
                ->loginByGfcCookie()
                ->loginBySid()
                ->initPageVars()
                ->addJoinForm();
        //->makeSearchBox();
        //->addLangForm();

        Cookie::sendFirstVisitCookie();

        try {
            $this->checkLoginStatus()
                    ->checkAccessPermission()
                    ->main();
        } catch (Exception $e) {
            $this->handleException($e);
        }


        /**
         * Observer will be able to
         * record access of current Viewer to
         * the current page for the purpose of
         * recording of who's online and at what url
         */
        $this->oRegistry->Dispatcher->post($this, 'onPageView', $this->aPageVars);

        //\StudyBuddy\Log::dump();
    }

    /**
     * Sets SESSION['oViewer']
     * and then points
     * $this->oViewer object to it
     * It is set even if user is not logged in
     * in which case it will be just an
     * object with default values from USERS table
     *
     * @return object $this
     */
    protected function initViewerObject() {

        if (empty($_SESSION['oViewer'])) {
            d('cp no Viewer in session');
            $_SESSION['oViewer'] = User::factory($this->oRegistry);
            $_SESSION['oViewer']->setTime();
            d('oViewer new: ' . print_r($_SESSION['oViewer']->getArrayCopy(), 1));
            /**
             * Send referrer cookie if necessary
             */
            Cookie::sendRefferrerCookie();
        }

        $this->oRegistry->Viewer = $_SESSION['oViewer'];

        d(' session viewer: ' . print_r($_SESSION['oViewer']->getArrayCopy(), 1));

        return $this;
    }

    protected function setLocale() {
        $this->oRegistry->Locale->setLocale();
        $this->Tr = $this->oRegistry->Tr;

        return $this;
    }

    /**
     * Translator method
     * It's customary in many projects to
     * use the single underscore
     * symbol for translation function.
     *
     * @param string $string string to translate
     *
     * @param array $vars optional array of replacement vars for
     * translation
     *
     * @return string translated string
     */
    protected function _($string, array $vars = null) {

        return $this->Tr->get($string, $vars);
    }

    /**
     * Magic method
     *
     * @return
     */
    public function __toString() {

        return $this->getResult();
    }

    /**
     * Must be implemented in sub-class
     * this should contain the main
     * logic of controller class.
     *
     * The purpose of this method is for a controller
     * class to populate
     * the $this->aPageVars array
     *
     * @return
     */
    abstract protected function main();

    /**
     * Check Request object for required params
     * as well as for required form token
     *
     * @return object $this
     */
    protected function initParams() {

        if ($this->bRequirePost && ('POST' !== Request::getRequestMethod())) {
            throw new Exception('POST method required');
        }

        $this->oRequest->setRequired($this->aRequired)->checkRequired();

        if (true === $this->requireToken) {
            \StudyBuddy\Forms\Form::validateToken($this->oRegistry);
        }

        d('cp');

        return $this;
    }

    /**
     * Setup initial
     * $this->aPageVars variables.
     * This variables are used by tplMain
     * - a main web page templates, regardless of
     * which controller is called - these vars are
     * always added to the page
     *
     *
     * @return object $this
     */
    protected function initPageVars() {
        if (!$this->bInitPageVars
                || Request::isAjax()
                || 'logout' === $this->action
                || 'login' === $this->action) {
            d('special case: ' . $this->action);

            return $this;
        }

        $Viewer = $this->oRegistry->Viewer;
        $this->aPageVars = \tplMain::getVars();

        $oIni = $this->oRegistry->Ini;
        $this->aPageVars['site_title'] = $oIni->SITE_TITLE;
        $this->aPageVars['site_url'] = $oIni->SITE_URL;
        $this->aPageVars['site_description'] = $oIni->SITE_NAME;
        $this->aPageVars['show_comments'] = $oIni->SHOW_COMMENTS;
        $this->aPageVars['max_comments'] = $oIni->MAX_COMMENTS;
        $this->aPageVars['comments_timeout'] = $oIni->COMMENT_EDIT_TIME;
        $this->aPageVars['layoutID'] = $this->layoutID;
        $this->aPageVars['DISABLE_AUTOCOMPLETE'] = $oIni->DISABLE_AUTOCOMPLETE;
        $this->aPageVars['JS_MIN_ID'] = JS_MIN_ID;
        $this->aPageVars['home'] = $this->_('Home');

        /**
         * @todo later can change to something like
         * $this->oRegistrty->Viewer->getStyleID()
         *
         */
		//$css = (true === STUDYBUDDY_DEBUG) ? '/_main.css' : '/main.css'; 
        $css = (true === STUDYBUDDY_DEBUG) ? '/_main.css' : '/_main.css';
        $this->aPageVars['main_css'] = $oIni->CSS_SITE . '/style/' . STYLE_ID . '/' . VTEMPLATES_DIR . $css;


        if ('' !== $gfcID = $oIni->GFC_ID) {
            $this->addGFCCode($gfcID);
        }

        $aFacebookConf = $oIni->getSection('FACEBOOK');

        if (!empty($aFacebookConf)) {
            if (!empty($aFacebookConf['APP_ID'])) {
                $this->addMetaTag('fbappid', $aFacebookConf['APP_ID']);
                $this->addFacebookJs($aFacebookConf['APP_ID']);

                if (!empty($aFacebookConf['EXTENDED_PERMS'])) {
                    $this->addMetaTag('fbperms', $aFacebookConf['EXTENDED_PERMS']);
                }
            }
        }

        $this->aPageVars['session_uid'] = $Viewer->getUid();
        $this->aPageVars['role'] = $Viewer->getRoleId();
        $this->aPageVars['rep'] = $Viewer->getReputation();
        $this->aPageVars['version_id'] = Form::generateToken();
        /**
         * meta 'tw' will be set to string "1" if user has conneted Twitter
         */
        $this->addMetaTag('tw', ('' !== (string) $Viewer->getTwitterSecret()));
        /**
         * meta 'tw' will be set to string "1" if user has conneted Facebook
         */
        $this->addMetaTag('fb', ('' !== (string) $Viewer->getFacebookToken()));

        //$js = (true === STUDYBUDDY_DEBUG) ? '/qa.js' : '/qa_min.js';
		$js = (true === STUDYBUDDY_DEBUG) ? '/qa.js' : '/qa.js';
			
        $src = $oIni->JS_SITE . '/js' . $js;

        $this->aPageVars['JS'] = $src;
        /**
         * @todo
         *  also add twitter id or username or just 'yes'
         *  of viewer so that we know viewer has twitter account
         *  and is capable of using twitter from our API
         *  Also we can ask use to add Twitter account
         *  if we know he does not have one connected yet
         */
        return $this;
    }

    /**
     * Add Google FriendConnect JavaScript to page
     *
     * @param unknown_type $gfcID
     */
    protected function addGFCCode($gfcID) {

        $this->addMetaTag('gfcid', $gfcID);
        $this->aPageVars['gfc_js'] = \tplGfcCode::parse(array($gfcID), false);

        return $this;
    }

    /**
     *
     * Add JavaScript for Facebook UI to the page
     *
     * @param string $appId value from !config.ini 'FACEBOOK' -> 'APP_ID'
     */
    protected function addFacebookJs($appId) {
        /**
         * Do NOT add Facebook JS
         * to the page with logout flag
         * in order to prevent FB from adding
         * fb cookie again
         */
        $this->aPageVars['fb_js'] = \tplFbJs::parse(array($appId), false);


        return $this;
    }

    /**
     * Add extra meta tag to the page
     * @param string $tag name of tag
     * @param string $val value of tag
     *
     * @return object $this
     */
    protected function addMetaTag($tag, $val) {
        $meta = CRLF . sprintf('<meta name="%s" content="%s">', $tag, $val);

        $this->aPageVars['extra_metas'] .= $meta;

        return $this;
    }

    /**
     * If user session did not
     * contain data that allowed to
     * treat user as logged in, then
     * try to login user by uid/sid cookies
     * This will work if user has previously
     * logged in and selected the 'remember me'
     * check box.
     *
     * @return object $this OR redirects back
     * to the same page but with SESSION setup
     * with user data, so user will be detected as logged-in
     * after the redirect
     */
    protected function loginBySid() {

        if ($this->isLoggedIn() || 'logout' === $this->action || 'login' === $this->action) {
            d('cp');
            return $this;
        }

        if (!isset($_COOKIE) || !isset($_COOKIE['uid']) || !isset($_COOKIE['sid'])) {
            d('$_COOKIE: ' . print_r($_COOKIE, 1));

            return $this;
        }

        try {
            $oCheckLogin = new CookieAuth($this->oRegistry);
            $oUser = $oCheckLogin->authByCookie();
            d('aResult: ' . print_r($oUser->getArrayCopy(), 1));
        } catch (CookieAuthException $e) {
            e('StudyBuddyError: login by sid failed with message: ' . $e->getMessage());
            Cookie::delete(array('uid'));

            return $this;
        }

        /**
         * Login OK
         * used to also
         * ->setUserTimezone($this->oViewer)
         * but its not necessary because user
         *  will be redirected anyway
         */
        $this->processLogin($oUser);
        $this->oRegistry->Dispatcher->post($this, 'onCookieLogin');

        return $this;
    }

    /**
     * Login with Google Friend Connect cookie
     * fcauth
     *
     * @return $this
     */
    protected function loginByGfcCookie() {
        if ($this->isLoggedIn()
                || 'logout' === $this->action
                || 'login' === $this->action) {
            d('cp');
            return $this;
        }

        $GfcSiteID = $this->oRegistry->Ini->GFC_ID;
        if (empty($GfcSiteID)) {
            d('not using friend connect');
            return $this;
        }

        try {
            $oGfc = new ExternalAuthGfc($this->oRegistry, $GfcSiteID);
            $oViewer = $oGfc->getUserObject();
        } catch (GFCAuthException $e) {

            d('Auth by GFC cookie failed ' . $e->getMessage() . ' ' . $e->getFile() . ' ' . $e->getLine());

            return $this;
        }

        $this->processLogin($oViewer);
        $this->oRegistry->Dispatcher->post($this, 'onGfcLogin');

        return $this;
    }

    /**
     * Authenticate user if user has fbc_ cookie
     * This cookie is set right after user clicks
     * on login with facebook button
     *
     * @return object $this
     */
    protected function loginByFacebookCookie() {

        $action = $this->action;

        if ($this->isLoggedIn()
                || 'logout' === $action
                || 'connectfb' === $action
                || 'login' === $action) {
            d('skipping loginByFacebookCookie');
            return $this;
        }

        try {
            $oViewer = ExternalAuthFb::getUserObject($this->oRegistry);
            d('got $oViewer: ' . print_r($oViewer->getArrayCopy(), 1));
            $this->processLogin($oViewer);
            d('logged in facebook user: ' . $this->oRegistry->Viewer->getUid());
            $this->oRegistry->Dispatcher->post($this, 'onFacebookLogin');
        } catch (FacebookAuthException $e) {
            d('Facebook login failed. ' . $e->getMessage() . ' ' . $e->getFile() . ' ' . $e->getLine());
        }

        return $this;
    }

    /**
     *
     * Resets the $_SESSION array, adds ['viewer'] to $_SESSION
     * and points $this->oRegistry->Viewer = $_SESSION['oViewer'] = $oUser;
     *
     * @param User $oUser
     * @param unknown_type $bResetSession
     * @throws LoginException in case filter regected
     * the login by cancelling onBeforeLogin event
     *
     * @return object $this
     */
    protected function processLogin(User $oUser, $bResetSession = false) {

        d('processing user hashCode: ' . $oUser->hashCode() . ' userHash: ' . $oUser->hashCode());

        /**
         * This little thing is not
         * necessary for web-page type of request
         * but just in case request was made by
         * some other means like by email
         *
         */
        if (!isset($_SESSION)) {
            $_SESSION = array();
        }

        d('cp ' . gettype($oUser));

        /**
         * This give a change for some sort of filter to examine twitter id, twitter name
         * and possibly disallow the login
         *
         */
        if (false === $this->oRegistry->Dispatcher->post($oUser, 'onBeforeUserLogin')) {
            d('onBeforeUserLogin returned false');
            throw new LoginException('Access denied');
        }

        if ($bResetSession) {
            session_regenerate_id();
        }

        /**
         * New way just replace the $_SESSION['oViewer'] with this new object
         * Not sure if this will actually work, but....
         * have to try it, otherwise how else can we replace
         * the type of oViewer from User to the new object
         * which may now be TwitterUser?
         *
         * An alternative would probably be to create a brand new object
         * of the same type and copy the underlying array to the new object
         * maybe the clone is a good way to do this?
         * Not sure if there are any pitfalls in cloning the ArrayObject object
         *
         */
        /**
         * Just a precaution to make sure
         * descructor will not try to save the object that we no longer need.
         */
        d('old SESSION oViewer hash code was: ' . $_SESSION['oViewer']->hashCode() . ' old userHash: ' . $_SESSION['oViewer']->hashCode());

        $this->oRegistry->Viewer = $_SESSION['oViewer'] = $oUser;
        d('Viewer in session now: ' . print_r($_SESSION['oViewer']->getArrayCopy(), 1));
        /**
         * This is important otherwise
         * the old stale value is used
         * when checking isLoggedIn()
         */
        if (isset($this->bLoggedIn)) {
            unset($this->bLoggedIn);
        }

        d('SESSION oViewer is now of type ' . $_SESSION['oViewer']->getClass() . ' hash: ' . $_SESSION['oViewer']->hashCode());


        $_SESSION['oViewer']->setTime();


        /**
         * Remove navlinks block from
         * session because
         * after user logged-in he suppose to see
         * different links block
         */
        if (!empty($_SESSION)) {
            $_SESSION['navlinks'] = array();
            $_SESSION['login_form'] = null;
            $_SESSION['login_error'] = null;
        }

        return $this;
    }

    /**
     * Performs the last step in assembling
     * the XML object by appending the
     * Switchaccount form and side menu HTML
     * if necessary and then
     * returns the textual representation
     * of the $this->oDocGlobal object
     *
     * @return mixed result of toHTML()
     */
    public function getResult() {

        if (404 === $this->httpCode) {
            d('setting 404 error code');
            header("HTTP/1.0 404 Not Found");
        }

        $this->addLoginBlock()->makeSearchBox()->addLastJs()->addExtraCss();

        $tpl = \tplMain::parse($this->aPageVars);
        /**
         * @todo Translate string
         */
        $scriptTime = ($this->oRegistry->Ini->SHOW_TIMER) ? 'Page generated in ' . abs((microtime() - INIT_TIMESTAMP)) . ' seconds' : '';

        return \str_replace('{timer}', $scriptTime, $tpl);
    }

    /**
     * Adds (appends) value to last_js element of page
     *
     * @return object $this
     */
    protected function addLastJs() {
        if (!empty($this->lastJs)) {
            foreach ((array) $this->lastJs as $val) {
                $this->aPageVars['last_js'] .= CRLF . sprintf('<script type="text/javascript" src="%s"></script>', $val);
            }
        }

        return $this;
    }

    /**
     *
     * Adds extra stylesheet(s) to the page
     *
     * @return object $this
     */
    protected function addExtraCss() {
        if (!empty($this->extraCss)) {
            d('got extra css to add');
            foreach ((array) $this->extraCss as $val) {
                $this->aPageVars['extra_css'] .= CRLF . sprintf('<link rel="stylesheet" type="text/css" href="%s">', $val);
            }
        }

        return $this;
    }

    /**
     * If ENABLE_CODE_EDITOR is set to true
     * in !config.ini then
     * add additional 2 js and 1 css file to the page
     * This will enable the "Code Editor" and "Code highlighter"
     * widgets in the YUI Editor
     *
     * This method is called from 2 different controllers:
     * Ask and Viewquestion
     * That's why it's here in just one place - so it does
     * not have to be duplicated in each controller
     *
     * @return object $this
     */
    protected function configureEditor() {
        $a = $this->oRegistry->Ini->getSection('EDITOR');
        if ($a['ENABLE_CODE_EDITOR']) {
            d('enabling code highlighter');
            $this->lastJs = array('/js/min/shCoreMin.js', '/js/min/dsBrushes.js');
            $this->extraCss = '/js/min/sh.css';
        }

        if ($a['ENABLE_YOUTUBE']) {
            $this->addMetaTag('btn_yt', '1');
        }

        return $this;
    }

    /**
     * Adds the Login forum or Welcome block
     *
     * @return object $this
     */
    protected function addLoginBlock() {
        if ('logout' !== $this->action) {
            $this->aPageVars['header'] = LoginForm::makeWelcomeMenu($this->oRegistry);
        }

        return $this;
    }

    /**
     * Formats the exception, adding
     * additional exception data like backtrace
     * if running in debug mode
     * then adds the 'error' under oXS main element
     *
     * @return
     * @param object $e Exception object
     */
    public function handleException(\StudyBuddy\Exception $le) {
        try {
            d('cp');

            if ($le instanceof RedirectException) {
                header("Location: " . $le->getMessage(), true, $le->getCode());
                fastcgi_finish_request();
                exit;
            }

            if ($le instanceof CaptchaLimitException) {
                d('Captcha limit reached.');
                /**
                 * @todo add ip to CAPTCHA_HACKS collection
                 *
                 */
            }

            /**
             * In case of StudyBuddyAuthException
             * the value of 'c' attribute in exception
             * element will be set to "login"
             * indicating to template that this
             * is a 'must login' type of exception
             * and to render the login form
             *
             */
            $class = ($le instanceof AuthException) ? 'login' : 'excsl';

            /**
             * Special case:
             * the http error code can be
             * passed in exception as third argument
             * (in case where there are no second arg,
             * the second arg must be passed as null)
             */
            if (201 < $errCode = $le->getCode()) {
                $this->httpCode = (int) $errCode;
            }

            if ($le instanceof StudyBuddy404Exception) {
                $this->httpCode = 404;
            }

            if (!($le instanceof AuthException) && !($le instanceof MustLoginException) && !($le instanceof LoginException)) {
                e('Exception caught in: ' . $le->getFile() . ' on line: ' . $le->getLine() . ' ' . $le->getMessage());
            }

            /**
             *
             * Exception::formatException will correctly
             * handle sending out JSON and exiting
             * if the request isAjax
             *
             */
            $err = Exception::formatException($le, null, $this->oRegistry->Tr);
            /**
             * @todo if Login exception then present a login form!
             *
             */
            $this->aPageVars['layoutID'] = 1;
            $this->aPageVars['body'] = \tplException::parse(array('message' => $err, 'class' => $class, 'title' => $this->_('Alert')));
        } catch (\Exception $e) {
            e('Exception object ' . $e->getMessage());
            $err = Responder::makeErrorPage($le->getMessage() . ' in ' . $e->getFile());
            echo $err;
            fastcgi_finish_request();
            exit;
        }
    }

    /**
     * This method is called from Login and
     * from wwwOauth, thus its here in one place
     *
     * @return array of user profile and welcome html div
     * if user is logged in, or empty array otherwise
     */
    protected function makeLoginArray() {
        $a = array();
        d('cp');
        if ($this->isLoggedIn()) {
            $welcome = LoginForm::makeWelcomeMenu($this->oRegistry);
            $a['welcome'] = $welcome;
        }

        d('cp');
        d('a: ' . print_r($a, 1));

        return $a;
    }

    protected function makeSearchBox() {

        $this->aPageVars['searchbox'] = LoginForm::makeSearchBox($this->oRegistry);

        return $this;
    }

    /**
     * Validates the value of form token
     * passed in form against the one stored in SESSION
     *
     * @todo validate (store it first) IP address
     * of request that it must match ip when token is validate
     * and throw special type of Exception so that a user will
     * get explanation that IP address has changed
     *
     * @param string $token value as passed in the submitted form
     * @return true of success
     * @throws StudyBuddyException if validation fails
     */
    protected function validateToken($token = null) {

        $message = '';
        $token = ( (null === $token) && !empty($this->oRequest['token']) ) ? $this->oRequest['token'] : $token;

        if (empty($_SESSION['secret'])) {
            d("No token in SESSION " . print_r($_SESSION, 1));
            /**
             * @todo
             * Translate String
             */
            $message = 'Form_token_missing';
        } elseif ($_SESSION['secret'] !== $token) {
            d('session token: ' . $_SESSION['secret'] . ' supplied token: ' . $token);
            $message = 'wrong form token';
        }

        if (!empty($message)) {

            if (Request::isAjax()) {
                Responder::sendJSON(array('exception' => $message));
            }

            throw new TokenException($message);
        }

        return true;
    }

    /**
     * Add extra div with "Join" form
     * where we ask to provide email address
     * after user joins with external provider
     *
     * @return object $this
     */
    protected function addJoinForm() {
        if (!$this->bInitPageVars || !Request::isAjax() && ('remindpwd' !== $this->action) && ('logout' !== $this->action)) {
            /**
             * If user opted out of continuing
             * registration, the special 'dnd' or "Do not disturb"
             * cookie was set via Javascritp
             * We will respect that and will not show that same
             * nagging prompt again
             *
             * This cookie is deleted on Logout
             * @todo set ttl for this cookie to last only a couple of days
             * so we can keep nagging user again after awhile until user
             * finally enters email address
             */
            $cookie = Cookie::get('dnd');
            d('dnd: ' . $cookie);
            if (!$cookie) {
                $isNewUser = $this->oRegistry->Viewer->isNewUser();
                d('isNewUser: ' . $isNewUser . ' $this->oRegistry->Viewer: ' . print_r($this->oRegistry->Viewer->getArrayCopy(), 1));

                if ($this->oRegistry->Viewer instanceof UserExternal) {
                    $email = $this->oRegistry->Viewer->email;
                    d('email: ' . var_export($email, true));
                    if (empty($email)) {
                        $sHtml = RegBlock::factory($this->oRegistry)->getBlock();
                        d('$sHtml: ' . $sHtml);
                        $this->aPageVars['extra_html'] = $sHtml;
                    }
                }
            }
        }

        return $this;
    }

    /**
     * Define the location of templates
     * This is usually used for pointing
     * to special 'mobile' directory when
     * we need to serve mobile pages
     *
     * @todo something like this:
     * oRegistry->Viewer->getStyleId().DS.$this->tplDir
     * where getStyleId will return whatever user
     * has selected with fallback to default '1'
     *
     * @return object $this
     */
    protected function setTemplateDir() {

        d('setting template dir');

        define('STYLE_ID', $this->styleID);
        define('VTEMPLATES_DIR', $this->tplDir);

        return $this;
    }

    /**
     * Add the drop-down menu for Language selection
     * to the page vars
     *
     * @return $this
     */
    protected function addLangForm() {
        $this->aPageVars['langsForm'] = $this->oRegistry->Locale->getOptions();

        return $this;
    }

}
